package utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.ws.rs.core.StreamingOutput;

import org.eclipse.jetty.io.ConnectionStatistics.Stats;

import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.async.ResultCallbackTemplate;
import com.github.dockerjava.api.command.AttachContainerCmd;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.ExecCreateCmd;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.ExecStartCmd;
import com.github.dockerjava.api.command.InspectContainerResponse;
import com.github.dockerjava.api.command.StatsCmd;
import com.github.dockerjava.api.model.Bind;
import com.github.dockerjava.api.model.Container;
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.PortBinding;
import com.github.dockerjava.api.model.Statistics;
import com.github.dockerjava.api.model.Volume;
import com.github.dockerjava.core.DockerClientBuilder;

import cn.hutool.Hutool;
import entity.ContainerStats;
import view.DockerContainerView;

public class DockerManager {
    private DockerClient dockerClient;
    private boolean clientInited = false;

    public DockerManager() {
        dockerClient = DockerClientBuilder.getInstance().build();
        if(dockerClient != null)
        {
            clientInited = true;
        }
    }

    public boolean isInited()
    {
        return clientInited;
    }

    public List<String> getAllContainterId()
    {
        ArrayList<String> ids = new ArrayList<>();
        try {
            // 获取所有容器，包括正在运行和已停止的容器
            List<Container> containers = dockerClient.listContainersCmd()
                                                    .withShowAll(true) // 如果你只想获取正在运行的容器，可以删除这个选项
                                                    .exec();

            // 输出每个容器的 ID
            for (Container container : containers) {
                if (container.getId().equals("df4afcfefef1d10ea21c02e18ef0a9406ea8f1249c4129ba2a96dc4cf986418f"))
                    continue;
                if (container.getImage().equals("snowdreamtech/frps"))
                    continue;
                ids.add(container.getId());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return ids;
    }

    public DockerContainerView getContainerState(String containerId) 
    {
        DockerContainerView dockerContainerView = new DockerContainerView();
        try {
            // 获取容器详细信息
            InspectContainerResponse res = dockerClient.inspectContainerCmd(containerId).exec();
            dockerContainerView.ContainerID = res.getId();
            InspectContainerResponse.ContainerState containerState = res.getState();
            if (containerState.getRunning())
            {
                dockerContainerView.Running = 1;
            }
            dockerContainerView.pid = containerState.getPidLong();
            System.out.println(res.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

        return dockerContainerView;
    }

    public String createContainer(String image, 
    Map<String, String> mounts, Map<Integer, Integer> portMaps,
    List<String> cmds, String workingDir,
    Map<String, String> labels)
    {
        return createContainerWithLimit(image, 
        mounts, portMaps, 
        null, null, null, null,
        null, null,
        cmds, workingDir, 
        labels);
    }
    
    /**
     * 
     * @param image
     * @param mounts
     * @param portMaps
     * @param cmds
     * @param workingDir
     * @param labels 可以为null
     * @return containerId
     */
    public String createContainerWithLimit(String image, 
    Map<String, String> mounts, Map<Integer, Integer> portMaps,
    Long memery, Long swap, Long cpuPeriod, Long cpuQuota,
    Integer cpusstart, Integer cpusend,
    List<String> cmds, String workingDir,
    Map<String, String> labels)
    {
        // 指定文件挂载
        ArrayList<Bind> binds = new ArrayList<>();
        if (mounts != null)
        {
            for (Map.Entry<String, String> entry : mounts.entrySet()) {
                String bind = entry.getKey();
                String vol = entry.getValue();
                
                binds.add(new Bind(bind, new Volume(vol)));
            }
        }

        // 指定端口映射
        // 127.0.0.1:80:8080/tcp
        ArrayList<PortBinding> portBindings = new ArrayList<>();
        if (portMaps != null)
        {
            for (Map.Entry<Integer, Integer> entry : portMaps.entrySet()) {
                Integer containerPort = entry.getKey();
                Integer port = entry.getKey();
    
                String bindstr = "127.0.0.1:" + port + ":" + containerPort + "/udp";
    
                portBindings.add(PortBinding.parse(bindstr));
            }
        }

        System.out.println("image: " + image + " cmds: " + cmds.toString() + " workingDir: " + workingDir +
            " mounts: " + mounts.toString() + " ports: " + portMaps +
            " memry: " + memery + " swap: " + swap + " cpuPeriod: " + cpuPeriod + " cpuQuota: " + cpuQuota);


        CreateContainerResponse container = null;
        try {
            HostConfig hostConfig = HostConfig.newHostConfig()
                .withBinds(binds)
                .withPortBindings(portBindings)
                .withNetworkMode("host")
                .withMemory(memery)
                .withMemorySwap(swap)
                .withCpuPeriod(cpuPeriod)
                .withCpuQuota(cpuQuota);
    
            if (cpusstart != null && cpusend != null)
            {
                hostConfig = hostConfig.withCpusetCpus(cpusstart + "," + cpusend);
            }

            container = dockerClient.createContainerCmd(image)
                .withHostConfig(hostConfig)
                .withWorkingDir(workingDir)
                .withAttachStdout(true)
                .withAttachStderr(true)
                .withAttachStdin(true)
                .withTty(true)
                .withCmd(cmds)
                .withLabels(labels)
                .exec();
        } catch (Exception e) {
            e.printStackTrace();
        }

        if(container != null)
        {
            System.out.println("Created container with ID: " + container.getId());
            System.out.println("warn lens: " + container.getWarnings().length);
            for (String res : container.getWarnings()) {
                System.out.println("docker warn: " + res);
            }
            return container.getId();
        }

        return null;
    }

    public int startContainter(String containerId)
    {
        dockerClient.startContainerCmd(containerId).exec();
        return 0;
    }
    
    public int stopContainer(String containerId)
    {
        dockerClient.stopContainerCmd(containerId).exec();
        System.out.println("Stopped container with ID: " + containerId);
        return 0;
    }

    public int deleteContainer(String containerId)
    {
        dockerClient.removeContainerCmd(containerId).withForce(true).exec();
        System.out.println("Removed container with ID: " + containerId);
        return 0;
    }

    public int execCmd(String containerId, String[] cmds)
    {
        try {
            // 创建一个 Exec 任务，在容器内执行命令
            ExecCreateCmdResponse execCreateCmd = dockerClient.execCreateCmd(containerId)
                .withAttachStdout(true)   // 捕获标准输出
                .withAttachStderr(true)   // 捕获标准错误输出
                .withAttachStdin(true)    // 允许输入
                .withCmd(cmds)  // 要执行的命令
                .exec();

            // 启动 Exec 任务并执行命令
            ExecStartCmd execStartCmd = dockerClient.execStartCmd(execCreateCmd.getId())
                    .withDetach(true)  // 执行命令并等待其完成
                    .withTty(true);     // 启用伪终端

            // 执行命令并获取输出
            execStartCmd.exec(new AttachCallbackTest());
            // System.out.println("命令输出: " + output);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return 0;
    }

    public String attachContainerWithCmd(String containerId, String cmd)
    {
        String res = "attach Error";

        // 将字符串转换为字节数组
        byte[] bytes = cmd.getBytes();
        
        // 使用字节数组创建 ByteArrayInputStream
        InputStream inputStream = new ByteArrayInputStream(bytes);

        // 获取容器输出流（标准输出和标准错误）
        AttachContainerCmd attachContainerCmd = dockerClient.attachContainerCmd(containerId)
                // .withStdOut(true)      // 获取标准输出流
                // .withStdErr(true)      // 获取标准错误流
                .withStdIn(inputStream);           // 获取标准输入
                // .withFollowStream(true); // 持续获取输出

        // AttachCallback attachCallback = new AttachCallback(1);

        try {
            // 获取输出流
            attachContainerCmd.exec(new AttachCallbackTest());
            
            // while (!attachCallback.getFinished()) {
            //     Thread.sleep(100);
            // }

            // System.out.println(attachCallback.getOutput());

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }

        // res = attachCallback.getOutput();
        return res;
    }

    // TODO
    public String attachContainer(String containerId)
    {
        String res = "attach Error";

        // 获取容器输出流（标准输出和标准错误）
        AttachContainerCmd attachContainerCmd = dockerClient.attachContainerCmd(containerId)
                .withStdOut(true)      // 获取标准输出流
                .withStdErr(true)      // 获取标准错误流
                // .withStdIn(inputStream)           // 获取标准输入
                .withFollowStream(true); // 持续获取输出

        AttachCallback attachCallback = new AttachCallback(1);

        try {
            // 获取输出流
            attachContainerCmd.exec(attachCallback);
            
            while (!attachCallback.getFinished()) {
                Thread.sleep(100);
            }

            System.out.println(attachCallback.getOutput());

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }

        res = attachCallback.getOutput();
        return res;
    }

    public ContainerStats getStats(String containerId)
    {
        // 创建 Stats 命令对象
        StatsCmd statsCmd = dockerClient.statsCmd(containerId).withNoStream(true);  // withNoStream(true) 表示一次性获取
        Long[] res = {0L,0L,0L};
        AtomicBoolean finished = new AtomicBoolean(false);
        try {

            // 获取一次容器的资源占用数据
            statsCmd.exec(new MyCallback(res, finished));
            
            // 输出资源占用数据
            // System.out.println("CPU Usage: " + statistics.getCpuStats().getCpuUsage().getTotalUsage());
            // System.out.println("Memory Usage: " + statistics.getMemoryStats().getUsage());
            // System.out.println("Network I/O: " + stats.getNetwork().getRxBytes() + " / " + stats.getNetwork().getTxBytes());
        } catch (Exception e) {
            e.printStackTrace();
        }

        while (!finished.get()) {
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }

        ContainerStats containerStats = new ContainerStats(res[0], res[1], res[2]);
        return containerStats;
    }
}

class MyCallback extends ResultCallbackTemplate<MyCallback, Statistics> {
    private Long[] res;
    private AtomicBoolean finished;

    MyCallback(Long[] res, AtomicBoolean finish)
    {
        this.res = res;
        finished = finish;
    }

    @Override
    public void onNext(Statistics statistics) {
        System.out.println("CPU 使用量: " + Base.toJsonStr(statistics.getCpuStats()));
        System.out.println("内存使用量: " + Base.toJsonStr(statistics.getMemoryStats()));
        System.out.println("网络接收字节数: " + Base.toJsonStr(statistics.getNetworks()));
        // System.out.println("网络发送字节数: " + statistics.getNetwork().getTxBytes());

        res[0] = statistics.getCpuStats().getCpuUsage().getTotalUsage();
        res[1] = statistics.getMemoryStats().getUsage();
        res[2] = statistics.getMemoryStats().getLimit();

        finished.set(true);
    }

    @Override
    public void onError(Throwable throwable) {
        System.err.println("获取资源统计数据时发生错误: " + throwable.getMessage());
    }

    @Override
    public void onComplete() {
        System.out.println("容器资源统计数据获取完成！");
    }

    @Override
    public void close() {
        System.out.println("回调已关闭");
    }
}

class AttachCallback extends ResultCallbackTemplate<AttachCallback, Frame> {
    private boolean finished = false;
    private boolean start = false;
    private LocalDateTime startDateTime;
    private final int DUR;
    private StringBuffer sb = new StringBuffer();

    public boolean getFinished() {
        return finished;
    }

    public String getOutput()
    {
        return sb.toString();
    }

    public AttachCallback(int DUR)
    {
        this.DUR = DUR;
    }

    @Override
    public void onNext(Frame frame) {
        // 处理每一行日志
        // System.out.println("Log: " + frame);
        if (start == false)
        {
            startDateTime = LocalDateTime.now();
            start = true;
        }
        
        LocalDateTime now = LocalDateTime.now();
        Duration duration = Duration.between(startDateTime, now);
        if (start == true && duration.getSeconds()>=DUR)
        {
            try {
                close();
                finished = true;
                return ;
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        sb.append(new String(frame.getPayload()).trim());
    }

    @Override
    public void onComplete() {
        // 在操作完成时调用
        System.out.println("Log retrieval completed.");
    }

    @Override
    public void onError(Throwable throwable) {
        // 错误处理
        System.err.println("Error retrieving logs: " + throwable.getMessage());
        throwable.printStackTrace();
    }
}

class AttachCallbackTest extends ResultCallbackTemplate<AttachCallbackTest, Frame> {
    private boolean finished = false;
    private boolean start = false;
    private LocalDateTime startDateTime;
    // private final int DUR;
    private StringBuffer sb = new StringBuffer();

    public boolean getFinished() {
        return finished;
    }

    public String getOutput()
    {
        return sb.toString();
    }

    // public AttachCallback(int DUR)
    // {
    //     this.DUR = DUR;
    // }

    @Override
    public void onNext(Frame frame) {
        // 处理每一行日志
        System.out.println("Log: " + frame);
    }

    @Override
    public void onComplete() {
        // 在操作完成时调用
        System.out.println("Log retrieval completed.");
    }

    @Override
    public void onError(Throwable throwable) {
        // 错误处理
        System.err.println("Error retrieving logs: " + throwable.getMessage());
        throwable.printStackTrace();
    }
}
